from sklearn.neural_network import MLPClassifier
import numpy as np
import pickle
import gzip
import matplotlib.pyplot as plt

def MLP0() :
    from sklearn.neural_network import MLPClassifier
    X = [[0., 0.], [1., 1.]]
    y = [0, 1]
    clf = MLPClassifier(solver='lbfgs', alpha=1e-5, hidden_layer_sizes=(5, 5), random_state=1)
    """参数
    ---
        hidden_layer_sizes: 元祖 hidden_layer_sizes=(5, 2) hidden层2层,第一层5个神经元,第二层2个神经元)
        activation：激活函数 隐藏层激活函数,identity、logistic、tanh、relu。
        solver ：权重优化算法,lbfgs、sgd、adam。
         solver='lbfgs',  MLP的求解方法：L-BFGS 在小数据上表现较好,Adam 较为鲁棒,SGD在参数调整较优时会有最佳表现（分类效果与迭代次数）；
         SGD标识随机梯度下降。疑问：SGD与反向传播算法的关系
         learning_rate：学习率,constant、invscaling、adaptive。
        alpha：正则化项参数 L2惩罚(正则化项)参数。
        max_iter：最大迭代次数。
        shuffle：是否在每次迭代时对样本进行清洗,当solver为sgd或adam时使用。
random_state：随机数种子。
tol：优化算法停止的条件。当迭代前后的函数差值小于等于tol时就停止。
verbose：是否将过程打印出。
warm_start：是否使用之前的解决方法作为初始拟合。
momentum：梯度下降的动量,介于0到1之间,solver为sgd时使用。
nesterovs_momentum：是否使用Nesterov动量。
early_stopping：判断当验证效果不再改善时是否终止训练。
validation_fraction：用作早起停止验证的预留训练集的比例,0到1之间。
beta_1：估计一阶矩向量的指数衰减速率,[0,1)之间。
beta_2：估计二阶矩向量的指数衰减速率,[0,1)之间。
epsilon：数值稳定值,solver为adam时使用。
        随机优化的minibatches的大小。
       
alpha:L2的参数：MLP是可以支持正则化的,默认为L2,具体参数需要调整
hidden_layer_sizes=(5, 2) hidden层2层,第一层5个神经元,第二层2个神经元)

返回值属性说明

classes_：每个输出的类标签,大小为(n_classes,)。
loss_：损失函数计算出的当前损失值。
coefs_：列表中的第i个元素表示i层的权重矩阵。
intercepts_：列表中第i个元素表示i+1层的偏差向量。
n_iter_：迭代次数。
n_layers_：层数。
n_outputs_：输出的个数。
out_activation_：输出激活函数的名称。
    """
    clf.fit(X, y)
    print(clf.n_layers_)
    print(clf.n_iter_)
    print(clf.loss_)
    print(clf.out_activation_)
    # 输出分别为
    # 4
    # 15
    # 0.000141233472537
    # logistic

    print('predict\t', clf.predict([[2., 2.], [-1., -2.]]))
    print('predict\t', clf.predict_proba([[2., 2.], [1., 2.]]))
    print('clf.coefs_ contains the weight matrices that constitute the model parameters:\t', [coef.shape for coef in
                                                                                        clf.coefs_])
    print(clf)
    c = 0
    for i in clf.coefs_:
        c += 1
        print( c, len(i), i)

MLP0()


def MLP1():
    # 加载数据
    with gzip.open(r"mnist.pkl.gz") as fp:
        training_data, valid_data, test_data = pickle.load(fp,encoding='bytes')

    X_training_data, y_training_data = training_data
    X_valid_data, y_valid_data = valid_data
    X_test_data, y_test_data = test_data

    def show_data_struct():
        print(X_training_data.shape, y_training_data.shape)
        print(X_valid_data.shape, y_valid_data.shape)
        print(X_test_data.shape, y_test_data.shape)
        print(X_training_data[0])
        print(y_training_data[0])

    show_data_struct()

def MLP2():
    #创建数据集
#
    #　　你除了可以使用sklearn自带的数据集,还可以自己去创建训练样本,具体用法参见《Dataset
    #loading
    #utilities》,这里我们简单介绍一些,sklearn中的samples
    #generator包含的大量创建样本数据的方法：

    from sklearn.datasets.samples_generator import make_classification

    X, y = make_classification(n_samples=6, n_features=5, n_informative=2,
                               n_redundant=2, n_classes=2, n_clusters_per_class=2, scale=1.0,
                               random_state=20)

    # n_samples：指定样本数
    # n_features：指定特征数
    # n_classes：指定几分类
    # random_state：随机种子,使得随机状可重
    for x_, y_ in zip(X, y):
        print(y_, end=': ')
        print(x_)

def MLP3():
    """数据预处理
　　 #数据预处理阶段是机器学习中不可缺少的一环,它会使得数据更加有效的被模型或者评估器识别。下面我们来看一下sklearn中有哪些平时我们常用的函数
    #为了使得训练数据的标准化规则与测试数据的标准化规则同步,preprocessing中提供了很多Scaler："""
    from sklearn import preprocessing
    data = [[0, 0], [0, 0], [1, 1], [1, 1]]
    # 1. 基于mean和std的标准化
    scaler = preprocessing.StandardScaler().fit(train_data)
    scaler.transform(train_data)
    scaler.transform(test_data)

    # 2. 将每个特征值归一化到一个固定范围
    scaler = preprocessing.MinMaxScaler(feature_range=(0, 1)).fit(train_data)
    scaler.transform(train_data)
    scaler.transform(test_data)
    #feature_range: 定义归一化范围,注用（）括起来

def MLP4():
    #2.2
    #正则化（normalize）当你想要计算两个样本的相似度时必不可少的一个操作,就是正则化。
    #其思想是：首先求出样本的p-范数,然后该样本的所有元素都要除以该范数,这样最终使得每个样本的范数都为1。
      X = [[1., -1., 2.], [2., 0., 0.], [0., 1., -1.]]
""""   X_normalized = preprocessing.normalize(X, norm='l2')

    >> > X_normalized
    array([[0.40..., -0.40..., 0.81...],
           [1...., 0...., 0....],
           [0...., 0.70..., -0.70...]])""
def MLP5():
    #数据集拆分
    #在得到训练数据集时,通常我们经常会把训练数据集进一步拆分成训练集和验证集,这样有助于我们模型参数的选取。#

    # 作用：将数据集划分为 训练集和测试集
    # 格式：train_test_split(*arrays, **options)
    from sklearn.mode_selection import train_test_split

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
   
    参数
    ---
    arrays：样本数组,包含特征向量和标签

    test_size：
    　　float-获得多大比重的测试样本 （默认：0.25）
    　　int - 获得多少个测试样本

    train_size: 同test_size

    random_state:
    　　int - 随机种子（种子固定,实验可复现）
    　　
    shuffle - 是否在分割之前对数据进行洗牌（默认True）

    返回
    ---
    分割后的列表,长度=2*len(arrays), 
    　　(train-test split)
    """

def MLP6():
    #定义模型
    #在这一步我们首先要分析自己数据的类型,搞清出你要用什么模型来做,然后我们就可以在sklearn中定义模型了。
#   #sklearn为所有模型提供了非常相似的接口,这样使得我们可以更加快速的熟悉所有模型的用法。在这之前我们先来看看模型的常用属性和功能：
    # 拟合模型
    model.fit(X_train, y_train)
    # 模型预测
    model.predict(X_test)

    # 获得这个模型的参数
    model.get_params()
    # 为模型进行打分
    model.score(data_X, data_y)  # 线性回归：R square； 分类问题： acc

def MLP7():
    #4.1
    #线性回归#
    from sklearn.linear_model import LinearRegression
    # 定义线性回归模型
    model = LinearRegression(fit_intercept=True, normalize=False,
                             copy_X=True, n_jobs=1)
    """
    参数
    ---
        fit_intercept：是否计算截距。False-模型没有截距
        normalize： 当fit_intercept设置为False时,该参数将被忽略。 如果为真,则回归前的回归系数X将通过减去平均值并除以l2-范数而归一化。
         n_jobs：指定线程数
    """
def MLP8():
    #4.4
    #决策树DT
    from sklearn import tree
    model = tree.DecisionTreeClassifier(criterion='gini', max_depth = None,
         min_samples_split = 2, min_samples_leaf = 1, min_weight_fraction_leaf = 0.0,
          max_features = None, random_state = None, max_leaf_nodes = None,
          min_impurity_decrease = 0.0, min_impurity_split = None,
          class_weight = None, presort = False)
    """参数
    ---
        criterion ：特征选择准则gini/entropy
        max_depth：树的最大深度,None-尽量下分
        min_samples_split：分裂内部节点,所需要的最小样本树
        min_samples_leaf：叶子节点所需要的最小样本数
        max_features: 寻找最优分割点时的最大特征数
        max_leaf_nodes：优先增长到最大叶子节点数
        min_impurity_decrease：如果这种分离导致杂质的减少大于或等于这个值,则节点将被拆分。
    """#

def MLP9():
    #4.5
    #支持向量机SVM
    from sklearn.svm import SVC
    model = SVC(C=1.0, kernel='rbf', gamma ='auto')
    """参数
    ---
        C：误差项的惩罚参数C
        gamma: 核相关系数。浮点数,If gamma is ‘auto’ then 1/n_features will be used instead.
    """

def MLP10():#
    #4.6
    #k近邻算法KNN
    from sklearn import neighbors
    # 定义kNN分类模型
    model = neighbors.KNeighborsClassifier(n_neighbors=5, n_jobs=1)  # 分类
    model = neighbors.KNeighborsRegressor(n_neighbors=5, n_jobs=1)  # 回归
    """参数
    ---
        n_neighbors： 使用邻居的数目
        n_jobs：并行任务数
    """

def MLP11():
    #4.7
    #多层感知机（神经网络）
    from sklearn.neural_network import MLPClassifier
    # 定义多层感知机分类算法
    model = MLPClassifier(activation='relu', solver='adam', alpha=0.0001)
    """参数
    ---
        hidden_layer_sizes: 元祖
        activation：激活函数
        solver ：优化算法{‘lbfgs’, ‘sgd’, ‘adam’}
        alpha：L2惩罚(正则化项)参数。
    """

def MLP12():
    #5.
    #模型评估与选择篇
#
    #5.#1
    #交叉验证
    from sklearn.model_selection import cross_val_score
    cross_val_score(model, X, y=None, scoring=None, cv=None, n_jobs=1)
    """参数
    ---
        model：拟合数据的模型
        cv ： k-fold
        scoring: 打分参数-‘accuracy’、‘f1’、‘precision’、‘recall’ 、‘roc_auc’、'neg_log_loss'等等
    """

def MLP13():
    #5.2
    #检验曲线
    #使用检验曲线,我们可以更加方便的改变模型参数,获取模型表现。
    from sklearn.model_selection import validation_curve
    train_score, test_score = validation_curve(model, X, y, param_name, param_range, cv=None, scoring=None, n_jobs=1)
    """参数
    ---
        model:用于fit和predict的对象
        X, y: 训练集的特征和标签
        param_name：将被改变的参数的名字
        param_range： 参数的改变范围
        cv：k-fold

    返回值
    ---
       train_score: 训练集得分（array）
        test_score: 验证集得分（array）
    """#

def MLP14():
#6. 保存模型
#
#　　最后,我们可以将我们训练好的model保存到本地,或者放到线上供用户使用,那么如何保存训练好的model呢？主要有下面两种方式：
#
#6.1 保存为pickle文件
    import pickle

    # 保存模型
    with open('model.pickle', 'wb') as f:
        pickle.dump(model, f)

    # 读取模型
    with open('model.pickle', 'rb') as f:
        model = pickle.load(f)
    model.predict(X_test)

def MLP15():
    #6.2#
    #sklearn自带方法joblib
    from sklearn.externals import joblib

    # 保存模型
    joblib.dump(model, 'model.pickle')

    # 载入模型
    model = joblib.load('model.pickle')

MLP2()